use crate::gen_title;

pub fn ch2_16_1() {
    gen_title("ch2_16_1", 18);

    struct Circle {
        x: f64,
        y: f64,
        radius: f64,
    }

    impl Circle {
        fn new(x: f64, y: f64, radius: f64) -> Circle {
            Circle { x, y, radius }
        }

        fn area(&self) -> f64 {
            std::f64::consts::PI * (self.radius * self.radius)
        }
    }

    let circle = Circle::new(0.0, 0.0, 15.0);

    println!("Circle area: {}", circle.area());
}

pub fn ch2_16_2() {
    gen_title("ch2_16_2", 18);

    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32,
    }

    impl Rectangle {
        fn area(&self) -> u32 {
            self.width * self.height
        }
    }

    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        rect1.area()
    );
}

pub fn ch2_16_3() {
    gen_title("ch2_16_3", 18);

    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32,
    }

    impl Rectangle {
        fn width(&self) -> bool {
            self.width > 0
        }
    }

    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    if rect1.width() {
        println!("The rectangle has a nonzero width; it is {}", rect1.width);
    }
}

pub fn ch2_16_4() {
    gen_title("ch2_16_4", 18);

    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32,
    }

    impl Rectangle {
        pub fn new(width: u32, height: u32) -> Self {
            Rectangle { width, height }
        }

        pub fn width(&self) -> u32 {
            return self.width;
        }
    }

    let rect1 = Rectangle::new(30, 50);
    println!("{}", rect1.width());
}

pub fn ch2_16_5() {
    gen_title("ch2_16_5", 18);

    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32,
    }

    impl Rectangle {
        fn area(&self) -> u32 {
            self.width * self.height
        }

        fn can_hold(&self, other: &Rectangle) -> bool {
            self.width > other.width && self.height > other.height
        }
    }

    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    let rect2 = Rectangle {
        width: 10,
        height: 40,
    };
    let rect3 = Rectangle {
        width: 60,
        height: 45,
    };

    println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
    println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}

pub fn ch2_16_6() {
    gen_title("ch2_16_6", 18);

    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32,
    }

    impl Rectangle {
        fn area(&self) -> u32 {
            self.width * self.height
        }
    }

    impl Rectangle {
        fn can_hold(&self, other: &Rectangle) -> bool {
            self.width > other.width && self.height > other.height
        }
    }

    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    let rect2 = Rectangle {
        width: 10,
        height: 40,
    };
    let rect3 = Rectangle {
        width: 60,
        height: 45,
    };

    println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
    println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}

pub fn ch2_16_7() {
    gen_title("ch2_16_7", 18);

    #[allow(unused)]
    #[derive(Debug)]
    enum Message {
        Quit,
        Move { x: i32, y: i32 },
        Write(String),
        ChangeColor(i32, i32, i32),
    }

    impl Message {
        fn call(&self) {
            println!("{:?}", self);
        }
    }

    let m = Message::Write(String::from("hello"));
    m.call();
}

pub fn ch2_16_pra_1() {
    gen_title("ch2_16_pra_1", 18);

    #[derive(Debug)]
    struct Point {
        x: f64,
        y: f64,
    }

    impl Point {
        fn origin() -> Point {
            Point { x: 0.0, y: 0.0 }
        }

        fn new(x: f64, y: f64) -> Point {
            Point { x, y }
        }
    }

    #[derive(Debug)]
    struct Rectangle {
        p1: Point,
        p2: Point,
    }

    impl Rectangle {
        fn area(&self) -> f64 {
            let Point { x: x1, y: y1 } = self.p1;
            let Point { x: x2, y: y2 } = self.p2;

            ((x1 - x2) * (y1 - y2)).abs()
        }

        fn perimeter(&self) -> f64 {
            let Point { x: x1, y: y1 } = self.p1;
            let Point { x: x2, y: y2 } = self.p2;

            2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
        }

        fn translate(&mut self, x: f64, y: f64) {
            self.p1.x += x;
            self.p2.x += x;

            self.p1.y += y;
            self.p2.y += y;
        }
    }

    #[derive(Debug)]
    struct Pair(Box<i32>, Box<i32>);

    impl Pair {
        fn destroy(self) {
            let Pair(first, second) = self;

            println!("Destroying Pair({}, {})", first, second);
        }
    }

    let rectangle = Rectangle {
        p1: Point::origin(),
        p2: Point::new(3.0, 4.0),
    };

    println!("Rectangle perimeter: {}", rectangle.perimeter());
    println!("Rectangle area: {}", rectangle.area());

    let mut square = Rectangle {
        p1: Point::origin(),
        p2: Point::new(1.0, 1.0),
    };

    square.translate(1.0, 0.0);
    println!("Square {:?}", square);

    let pair = Pair(Box::new(1), Box::new(2));
    println!("pair contains {:?} and {:?}", pair.0, pair.1);

    pair.destroy();
}

pub fn ch2_16_pra_2() {
    gen_title("ch2_16_pra_2", 18);

    struct Rectangle {
        width: u32,
        height: u32,
    }

    impl Rectangle {
        fn area(&self) -> u32 {
            self.width * self.height
        }
    }

    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    assert_eq!(1500, rect1.area());
    println!("{}", rect1.area());
}

pub fn ch2_16_pra_3() {
    gen_title("ch2_16_pra_3", 18);

    #[derive(Debug)]
    struct TrafficLight {
        color: String,
    }

    impl TrafficLight {
        pub fn show_state(&self) {
            println!("TrafficLight color is {}", self.color);
        }
    }

    let light = TrafficLight {
        color: "red".to_string(),
    };
    light.show_state();
    println!("{:?}", light);
}

pub fn ch2_16_pra_4() {
    gen_title("ch2_16_pra_4", 18);

    #[derive(Debug)]
    struct TrafficLight {
        color: String,
    }

    impl TrafficLight {
        pub fn show_state(&self) {
            println!("TrafficLight color is {}", self.color);
        }

        pub fn change_state(&mut self, color: String) {
            self.color = color;
        }
    }

    let mut light = TrafficLight {
        color: "red".to_string(),
    };
    light.show_state();

    light.change_state("green".to_string());
    light.show_state();
}

pub fn ch2_16_pra_5() {
    gen_title("ch2_16_pra_5", 18);

    #[derive(Debug)]
    struct TrafficLight {
        color: String,
    }

    impl TrafficLight {
        pub fn new(color: String) -> TrafficLight {
            TrafficLight { color }
        }

        pub fn get_state(&self) -> &str {
            &self.color
        }
    }

    let light = TrafficLight::new("red".to_string());
    println!("TrafficLight color is {}", light.get_state());
}

pub fn ch2_16_pra_6() {
    gen_title("ch2_16_pra_6", 18);

    struct Rectangle {
        width: u32,
        height: u32,
    }

    impl Rectangle {
        fn area(&self) -> u32 {
            self.width * self.height
        }

        fn can_hold(&self, other: &Rectangle) -> bool {
            self.width > other.width && self.height > other.height
        }
    }

    let rect = Rectangle {
        width: 30,
        height: 60,
    };
    println!("{}", rect.area());
    println!(
        "{}",
        rect.can_hold(&Rectangle {
            width: 10,
            height: 20,
        })
    );
}

pub fn ch2_16_pra_7() {
    gen_title("ch2_16_pra_7", 18);

    #[derive(Debug)]
    enum TrafficLightColor {
        Red,
        Yellow,
        Green,
    }

    impl TrafficLightColor {
        fn show_state(&self) {
            match self {
                TrafficLightColor::Red => println!("TrafficLight color is red"),
                TrafficLightColor::Yellow => println!("TrafficLight color is yellow"),
                TrafficLightColor::Green => println!("TrafficLight color is green"),
            }
        }
    }

    let c = TrafficLightColor::Yellow;
    c.show_state();
}
